﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UnitTests.Framework;

namespace UnitTests.FrameworkTests
{
	[TestClass]
	public class Pop3ClientHandlerTest
	{
		[TestMethod]
		public void Pop3ConnectAndCheckCommandInterpretting()
		{
			using (Pop3TestClient testClient = new Pop3TestClient())
			{
				// Good command
				testClient.ProcessCommand("NOOP");
				testClient.ProcessCommand("CAPA", expectedResponseLines: 3);

				// Bad commands
				testClient.ProcessCommand("", expectSuccess: false);
				testClient.ProcessCommand("NonExistantCommand", expectSuccess: false);
				testClient.ProcessCommand("noop", expectSuccess: false);

				// Command after session closed
				testClient.ProcessCommand("QUIT");
				testClient.ProcessCommand("NOOP", null, "Server responded to NOOP after session was closed");
			}
		}

		[TestMethod]
		public void Pop3UserPassAuthorization()
		{
			using (Pop3TestClient testClient = new Pop3TestClient())
			{
				Pop3ClientHandler clientHandler = testClient.FindClientHandler();

				string correctUser = string.Format("USER {0}", clientHandler.Username);
				string correctPass = string.Format("PASS {0}", clientHandler.Password);

				testClient.ProcessCommand(correctPass, "Server accepted PASS without USER", false);
				testClient.ProcessCommand("USER blarg", "Server accepted bad user", false);
				testClient.ProcessCommand(correctUser);
				testClient.ProcessCommand(correctUser, "Server re-accepted a user", false);
				testClient.ProcessCommand("PASS blarg", "Server accepted bad password", false);
				testClient.ProcessCommand(correctPass);
				testClient.ProcessCommand("STAT");
				testClient.ProcessCommand(correctUser, "Server accepted user after login", false);
				testClient.ProcessCommand(correctPass, "Server accepted password after login", false);
				testClient.ProcessCommand("QUIT");
			}
		}
		
		[TestMethod]
		public void Pop3MessageListingAndStat()
		{
			using (Pop3TestClient testClient = new Pop3TestClient())
			{
				Pop3ClientHandler clientHandler = testClient.FindClientHandler();

				testClient.ProcessCommand("LIST", "Server gave list prior to login", false);
				testClient.ProcessCommand("STAT", "Server gave stats prior to login", false);

				string correctUser = string.Format("USER {0}", clientHandler.Username);
				testClient.ProcessCommand(correctUser);

				testClient.ProcessCommand("LIST", "Server gave list prior to login", false);
				testClient.ProcessCommand("STAT", "Server gave stats prior to login", false);

				string correctPass = string.Format("PASS {0}", clientHandler.Password);
				testClient.ProcessCommand(correctPass);

				testClient.ProcessCommand("LIST", expectedResponseLines: 1);
				testClient.ProcessCommand("UIDL", expectedResponseLines: 1);
				testClient.ProcessCommand("STAT");

				clientHandler.AddMail("Hello, World!");
				testClient.ProcessCommand("LIST", expectedResponseLines: 2);
				testClient.ProcessCommand("LIST 1");
				testClient.ProcessCommand("LIST 2", "Server responded to listing of non-existant list item", false);
				testClient.ProcessCommand("LIST 1 asdf", "Server responded to listing of non-existant list item", false);

				testClient.ProcessCommand("UIDL", expectedResponseLines: 2);
				testClient.ProcessCommand("UIDL 1");
				testClient.ProcessCommand("UIDL 2", "Server responded to unique-id of non-existant list item", false);
				testClient.ProcessCommand("UIDL 1 asdf", "Server responded to unique-id of non-existant list item", false);

				testClient.ProcessCommand("DELE 1");
				testClient.ProcessCommand("LIST 1", "Server showed deleted message", false);
				testClient.ProcessCommand("UIDL 1", "Server showed deleted message", false);

				testClient.ProcessCommand("QUIT");
			}
		}

		[TestMethod]
		public void Pop3DeleteMessage()
		{
			using (Pop3TestClient testClient = new Pop3TestClient())
			{
				Pop3ClientHandler clientHandler = testClient.FindClientHandler();
				clientHandler.AddMail("Hello, World!");
				Assert.IsFalse(clientHandler.Mail[0].Deleted, "Message was deleted after being added");

				testClient.ProcessCommand("DELE 1", "Server deleted message before login", false);
				string correctUser = string.Format("USER {0}", clientHandler.Username);
				testClient.ProcessCommand(correctUser);
				testClient.ProcessCommand("DELE 1", "Server deleted message before login", false);
				string correctPass = string.Format("PASS {0}", clientHandler.Password);
				testClient.ProcessCommand(correctPass);

				testClient.ProcessCommand("DELE 1 asdf", "Server deleted a non-existant message", false);

				testClient.ProcessCommand("LIST 1");
				testClient.ProcessCommand("DELE 1");

				testClient.ProcessCommand("LIST 1", "Server showed deleted message", false);
				testClient.ProcessCommand("DELE 1", "Server deleted a deleted message", false);
				Assert.IsTrue(clientHandler.Mail[0].Deleted, "Message was not deleted");
				
				testClient.ProcessCommand("RSET");
				Assert.IsFalse(clientHandler.Mail[0].Deleted, "Message was deleted after RSET");

				testClient.ProcessCommand("DELE 2", "Server deleted a non-existant message", false);

				testClient.ProcessCommand("QUIT");
			}
		}

		[TestMethod]
		public void Pop3RetrieveMail()
		{
			using (Pop3TestClient testClient = new Pop3TestClient())
			{
				Pop3ClientHandler clientHandler = testClient.FindClientHandler();
				clientHandler.AddMail("Hello, World!");
				Assert.IsFalse(clientHandler.Mail[0].Deleted, "Message was deleted after being added");

				testClient.ProcessCommand("RETR 1", "Server gave list prior to login", false);
				string correctUser = string.Format("USER {0}", clientHandler.Username);
				testClient.ProcessCommand(correctUser);
				testClient.ProcessCommand("RETR 1", "Server gave list prior to login", false);
				string correctPass = string.Format("PASS {0}", clientHandler.Password);
				testClient.ProcessCommand(correctPass);

				testClient.ProcessCommand("RETR 1", expectedResponseLines: 2);
				Assert.IsTrue(clientHandler.Mail[0].Collected, "Message has not been marked as collected");

				testClient.ProcessCommand("RETR 2", "Server responded to retrieving of non-existant list item", false);
				testClient.ProcessCommand("RETR 1 asdf", "Server responded to retrieving of non-existant list item", false);

				testClient.ProcessCommand("DELE 1");
				testClient.ProcessCommand("RETR 1", "Server showed deleted message", false);

				testClient.ProcessCommand("QUIT");
			}
		}

		[TestMethod]
		public void Pop3ApopAuthorization()
		{
			using (Pop3TestClient testClient = new Pop3TestClient())
			{
				Pop3ClientHandler clientHandler = testClient.FindClientHandler();

				string correctHash = MD5Handler.Hash(testClient.ServerTimestamp + clientHandler.Password);
				string correctApop = string.Format("APOP {0} {1}", clientHandler.Username, correctHash);

				testClient.ProcessCommand("APOP", "Server accepted blank apop", false);
				testClient.ProcessCommand(string.Format("APOP {0}", clientHandler.Username), "Server accepted username only", false);
				testClient.ProcessCommand(string.Format("APOP {0}", correctHash), "Server accepted hash only", false);
				testClient.ProcessCommand(string.Format("APOP {0} asdfasdf", clientHandler.Username), "Server accepted bad hash", false);
				testClient.ProcessCommand(string.Format("APOP asdfasdf {0}", correctHash), "Server accepted bad username", false);

				testClient.ProcessCommand(correctApop);
				testClient.ProcessCommand("STAT");
				testClient.ProcessCommand(correctApop, "User accepted APOP after login", false);
				testClient.ProcessCommand("QUIT");
			}

			using (Pop3TestClient testClient = new Pop3TestClient())
			{
				Pop3ClientHandler clientHandler = testClient.FindClientHandler();

				string correctHash = MD5Handler.Hash(testClient.ServerTimestamp + clientHandler.Password);
				string correctApop = string.Format("APOP {0} {1}", clientHandler.Username, correctHash);

				testClient.ProcessCommand(string.Format("USER {0}", clientHandler.Username));
				testClient.ProcessCommand(correctApop, "Server accepted APOP after receiving username", false);
				testClient.ProcessCommand(string.Format("PASS {0}", clientHandler.Password));
				testClient.ProcessCommand("STAT");
				testClient.ProcessCommand(correctApop, "Server accepted APOP after standard login", false);
				testClient.ProcessCommand("QUIT");
			}
		}
	}
}
